/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.bemanager.codegenerator;


import com.inspur.edp.bef.bemanager.codegenerator.actions.JavaBaseCommonCompCodeGen;
import com.inspur.edp.bef.bemanager.generatecomponent.ComponentGenUtil;
import com.inspur.edp.bef.bemanager.util.CheckInfoUtil;
import com.inspur.edp.bef.bemanager.util.ComponentExtendProperty;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.common.BizEntityJsonConst;
import com.inspur.edp.bef.bizentity.common.OperationConvertUtils;
import com.inspur.edp.bef.bizentity.operation.BizOperation;
import com.inspur.edp.bef.bizentity.operation.Determination;
import com.inspur.edp.bef.bizentity.operation.TccAction;
import com.inspur.edp.bef.bizentity.operation.Validation;
import com.inspur.edp.bef.bizentity.operation.internalmgraction.TccSettingElement;
import com.inspur.edp.cef.designtime.api.collection.CommonDtmCollection;
import com.inspur.edp.cef.designtime.api.collection.CommonValCollection;
import com.inspur.edp.cef.designtime.api.json.CefNames;
import com.inspur.edp.cef.designtime.api.operation.CommonDetermination;
import com.inspur.edp.cef.designtime.api.operation.CommonValidation;
import com.inspur.edp.cef.variable.dtgenerator.vardtmGenerate.javageneratecmpcode.JavaCommonDeterminationGenerator;
import com.inspur.edp.cef.variable.dtgenerator.vardtmGenerate.javageneratecmpcode.JavaIBaseCompCodeGen;
import com.inspur.edp.jittojava.context.GenerateService;
import com.inspur.edp.lcm.fs.api.IFsService;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.service.FileService;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;

public class JavaCodeFileGenerator {

  private String relativePath;
  private GspBusinessEntity be;
  private String compAssemblyName;
  private final IFsService iFsService;
  private final FileService fsService;
  private static final String javaCodeFileExtension = ".java";
  private final ArrayList<ComponentExtendProperty> extendProperties;

  /**
   * 创建文件路径
   *
   * @param metadata
   */
  public JavaCodeFileGenerator(GspMetadata metadata) {
    this();
    this.relativePath = metadata.getRelativePath();
    this.be = (GspBusinessEntity) metadata.getContent();
    this.compAssemblyName = ComponentGenUtil.GetComponentNamespace(relativePath);
  }

  public JavaCodeFileGenerator(String path, GspBusinessEntity be) {
    this();
    this.relativePath = path;
    this.be = be;
    this.compAssemblyName = ComponentGenUtil.GetComponentNamespace(relativePath);
  }

  public JavaCodeFileGenerator() {
    this.iFsService = SpringBeanUtils.getBean(IFsService.class);
    this.fsService = SpringBeanUtils.getBean(FileService.class);
    this.extendProperties = new ArrayList<>();
  }

  public void generate(ArrayList<String> actionList) {
    JavaGenerateBizMgrActions(actionList);
    JavaGenerateBizActions(actionList);
    JavaGenerateDeterminations(actionList);
    JavaGenerateValidations(actionList);
    JavaGenerateVariableDtms(actionList);
    javaGenerateTccActions(actionList);
    javaGenerateBeanConfigFile();
  }

  private static final String mgrActionDirName = "BizActions";

  private void JavaGenerateBizMgrActions(ArrayList<String> actionList) {
    String path = JavaPrepareBizActionsDir(mgrActionDirName);

    for (BizOperation action : be.getCustomMgrActions()) {
      if (actionList.contains(action.getCode())) {
        JavaBizMgrCodeGenerate(action, path, null);
      }
    }
  }

  private static final String JavaBizActionDirName = "EntityActions";

  private void JavaGenerateBizActions(ArrayList<String> actionList) {
    //var path = JavaPrepareBizActionsDir(JavaBizActionDirName);
    java.util.ArrayList<GspBizEntityObject> nodes = be.getAllNodes();

    for (GspBizEntityObject node : nodes) {

      for (BizOperation action : node.getCustomBEActions()) {
        if (actionList.contains(action.getCode())) {
          String path = JavaPrepareDir(JavaBizActionDirName, node.getCode());
          JavaBECodeGenerate(action, path, be.getMainObject());
        }
      }
    }
  }

  private static final String JavaTccActionDirName = "TccActions";

  private void javaGenerateTccActions(ArrayList<String> actionList) {
    java.util.ArrayList<GspBizEntityObject> nodes = be.getAllNodes();
    for (GspBizEntityObject node : nodes) {
      for (TccSettingElement ele : node.getTccSettings()) {
        TccAction action = ele.getTccAction();
        if (actionList.contains(action.getCode())) {
          String path = JavaPrepareDir(JavaTccActionDirName, node.getCode());
          JavaBECodeGenerate(action, path, node);
        }
      }
    }
  }

  private static final String JavaDtmDirName = "Determinations";

  private void JavaGenerateDeterminations(ArrayList<String> actionList) {
    for (GspBizEntityObject node : be.getAllNodes()) {
      for (BizOperation action : node.getDeterminations()) {
        if (actionList.contains(action.getCode())) {
          String path = JavaPrepareDir(JavaDtmDirName, node.getCode());
          JavaDtmCodeGenerate(action, path, node);
        }
      }

      generateCommonDtm(actionList, node, node.getDtmAfterCreate(), CefNames.DtmAfterCreate);
      generateCommonDtm(actionList, node, node.getAllDtmAfterModify(), CefNames.DtmAfterModify);
      generateCommonDtm(actionList, node, node.getAllDtmBeforeSave(), CefNames.DtmBeforeSave);
      generateCommonDtm(actionList, node, node.getDtmBeforeQuery(), BizEntityJsonConst.B4QueryDtm);
      generateCommonDtm(actionList, node, node.getDtmAfterQuery(), BizEntityJsonConst.AftQueryDtm);
      generateCommonDtm(actionList, node, node.getDtmCancel(), BizEntityJsonConst.DtmCancel);
      generateCommonDtm(actionList, node, node.getDtmBeforeRetrieve(),
          BizEntityJsonConst.B4RetrieveDtm);
      generateCommonDtm(actionList, node, node.getDtmAfterLoading(),
          BizEntityJsonConst.AftLoadingDtm);
    }
  }

  private void generateCommonDtm(List<String> actionList, GspBizEntityObject obj,
      CommonDtmCollection dtmCollection, String commonDtmType) {
    if (dtmCollection == null) {
      return;
    }
    for (CommonDetermination dtm : dtmCollection) {
      if (!actionList.contains(dtm.getCode()) || dtm.getIsRef() || !dtm.getIsGenerateComponent()
          || obj.getDeterminations().findById(dtm.getID()) != null) {
        continue;
      }
      String path = JavaPrepareDir(JavaDtmDirName, obj.getCode());
      Determination action = OperationConvertUtils.convertToDtm(dtm, commonDtmType, obj);
      JavaDtmCodeGenerate(action, path, obj);
    }
  }

  private static final String JavaValidationDirName = "Validations";

  private void JavaGenerateValidations(ArrayList<String> actionList) {
    //var path = JavaPrepareDir(JavaValidationDirName);

    for (GspBizEntityObject node : be.getAllNodes()) {

      for (BizOperation action : node.getValidations()) {
        if (actionList.contains(action.getCode())) {
          String path = JavaPrepareDir(JavaValidationDirName, node.getCode());
          JavaValCodeGenerate(action, path, node);
        }
      }

      generateCommonVal(actionList, node, node.getValAfterSave(),
          BizEntityJsonConst.ValidationAfterSave);
      generateCommonVal(actionList, node, node.getallValBeforeSave(), CefNames.ValBeforeSave);
      generateCommonVal(actionList, node, node.getAllValAfterModify(), CefNames.ValAfterModify);
    }
  }

  private void generateCommonVal(List<String> actionList, GspBizEntityObject obj,
      CommonValCollection collection, String commonType) {
    if (collection == null) {
      return;
    }
    for (CommonValidation val : collection) {
      if (!actionList.contains(val.getCode()) || val.getIsRef() || !val.getIsGenerateComponent()
          || obj.getValidations().findById(val.getID()) != null) {
        continue;
      }
      String path = JavaPrepareDir(JavaValidationDirName, obj.getCode());
      Validation action = OperationConvertUtils.convertToVal(val, commonType, obj);
      JavaValCodeGenerate(action, path, obj);
    }
  }

  private static final String determinationDirName = "VarDeterminations";

  private void JavaGenerateVariableDtms(ArrayList<String> actionList) {
    if (be.getVariables() == null) {
      return;
    }
    String path = JavaPrepareDir(determinationDirName, be.getCode());

    JavaGenerateVariableDtms(be.getVariables().getDtmAfterCreate(), path, actionList);
    JavaGenerateVariableDtms(be.getVariables().getDtmAfterModify(), path, actionList);
    JavaGenerateVariableDtms(be.getVariables().getDtmBeforeSave(), path, actionList);
  }

  private String GetMetaProjName(GspProject projInfo) {
    String boProjName = projInfo.getMetadataProjectName().toLowerCase();
    return boProjName.substring(0, 0) + boProjName.substring(0 + boProjName.indexOf('-') + 1);
  }

  private String GetJavaCompModulePath() {
    String generatingAssembly = be.getDotnetGeneratingAssembly().replace(".", "\\");

    MetadataProjectService service = SpringBeanUtils.getBean(MetadataProjectService.class);

    String compModulePath = service.getJavaCompProjectPath(relativePath);

    //TODO toLowerCase 需要修改
    String compositePath = CheckInfoUtil
        .getCombinePath(compModulePath, "com", generatingAssembly.toLowerCase());

//		String compositePath = String.Concat(compModulePath, "\\com\\", generatingAssembly.toLowerCase());
    return compositePath;

  }

  private String JavaPrepareDir(String actionDirName, String codeDir) {
    String path = CheckInfoUtil.getCombinePath(GetJavaCompModulePath(), codeDir.toLowerCase(),
        actionDirName.toLowerCase());
//		String path = Path.Combine(GetJavaCompModulePath(), codeDir.toLowerCase(), actionDirName.toLowerCase());
    if (!fsService.isDirectoryExist(path)) {
      fsService.createDirectory(path);
    }
    return path;
  }

  private String JavaPrepareBizActionsDir(String actionDirName) {

    String path = CheckInfoUtil
        .getCombinePath(GetJavaCompModulePath(), actionDirName.toLowerCase());

//		var path = Path.Combine(GetJavaCompModulePath(), actionDirName.toLowerCase());
    if (!fsService.isDirectoryExist(path)) {
      fsService.createDirectory(path);
    }
    return path;
  }


  private void JavaBizMgrCodeGenerate(BizOperation op, String dirPath,
      GspBizEntityObject belongNode) {
    if (!op.getIsRef() && op.getIsGenerateComponent()) {
      JavaGenerateSingleFile(op, dirPath, false, null);
    }
  }

  //ORIGINAL LINE: private void JavaDtmCodeGenerate(BizOperation op, string dirPath, GspBizEntityObject belongNode = null)
  private void JavaDtmCodeGenerate(BizOperation op, String dirPath, GspBizEntityObject belongNode) {
    if (!op.getIsRef() && op.getIsGenerateComponent()) {
      JavaGenerateSingleFile(op, dirPath, !belongNode.getIsRootNode(), belongNode.getCode());
    }
  }

  //ORIGINAL LINE: private void JavaValCodeGenerate(BizOperation op, string dirPath, GspBizEntityObject belongNode = null)
  private void JavaValCodeGenerate(BizOperation op, String dirPath, GspBizEntityObject belongNode) {
    if (!op.getIsRef() && op.getIsGenerateComponent()) {
      JavaGenerateSingleFile(op, dirPath, !belongNode.getIsRootNode(), belongNode.getCode());
    }
  }

  //ORIGINAL LINE: private void JavaBECodeGenerate(BizOperation op, string dirPath, GspBizEntityObject belongNode = null)
  private void JavaBECodeGenerate(BizOperation op, String dirPath, GspBizEntityObject belongNode) {
    if (!op.getIsRef() && op.getIsGenerateComponent()) {
      JavaGenerateSingleFile(op, dirPath, !belongNode.getIsRootNode(), belongNode.getCode());
    }
  }

  @Getter
  private String codeContent;

  //ORIGINAL LINE: private void JavaGenerateSingleFile(BizOperation op, string dirPath,GspBizEntityObject belongNode = null)
  private void JavaGenerateSingleFile(BizOperation op, String dirPath, boolean isChild,
      String nodeCode) {
    JavaBaseCommonCompCodeGen codeGen = !isChild ?
        JavaCmpCodeGeneratorFactory.JavaGetGenerator(be, op, compAssemblyName, relativePath) :
        JavaCmpCodeGeneratorFactory
            .JavaGetChildNodeGenerator(be, nodeCode, op, compAssemblyName, relativePath);

    String originalFilePath = CheckInfoUtil
        .getCombinePath(relativePath, ComponentGenUtil.ComponentDir,
            codeGen.GetCompName() + javaCodeFileExtension);
    String filePathExe = CheckInfoUtil
        .getCombinePath(dirPath, codeGen.GetCompName() + javaCodeFileExtension);
    //此两处判断用来兼容原来没有对应文件夹情形
    if (fsService.isFileExist(originalFilePath)) {
      JavaReplace(originalFilePath);
      return;
    }
    if ((fsService.isFileExist(filePathExe))) {
      JavaReplace(filePathExe);
      return;
    }
    codeContent = codeGen.Generate();
    iFsService.createFile(filePathExe, codeContent, StandardCharsets.UTF_8);
    ComponentExtendProperty extendProp = codeGen.getExtendProperty();
    if (extendProp != null && extendProp.getCefConfigBeanGenCode() != null) {
      this.extendProperties.add(extendProp);
    }
  }

  public void createVarDtmJavaCodeFile(CommonDetermination dtm) {
    String path = JavaPrepareDir(determinationDirName, be.getCode());
    JavaGenerateVariableDtm(dtm, path);
  }

  public void createJavaCodeFile(BizOperation op, boolean isChild, String nodeCode) {
    String path;
    switch (op.getOpType()) {
      case BizMgrAction:
        path = JavaPrepareBizActionsDir(mgrActionDirName);
        break;
      case BizAction:
        path = JavaPrepareDir(JavaBizActionDirName, nodeCode);
        break;
      case TccAction:
        path = JavaPrepareDir(JavaTccActionDirName, nodeCode);
        break;
      case Validation:
        path = JavaPrepareDir(JavaValidationDirName, nodeCode);
        break;
      case Determination:
        path = JavaPrepareDir(JavaDtmDirName, nodeCode);
        break;
      default:
        throw new RuntimeException("不支持的动作类型:" + op.getOpType());
    }
    JavaGenerateSingleFile(op, path, isChild, nodeCode);
    javaGenerateBeanConfigFile();
  }

  private void JavaGenerateVariableDtmFile(JavaIBaseCompCodeGen codeGen, String dirPath) {
    String originalFilePath = CheckInfoUtil
        .getCombinePath(relativePath, ComponentGenUtil.ComponentDir,
            codeGen.getCompName() + javaCodeFileExtension);
    String filePathExe = CheckInfoUtil
        .getCombinePath(dirPath, codeGen.getCompName() + javaCodeFileExtension);
    String filePathCommon = CheckInfoUtil
        .getCombinePath(dirPath, codeGen.getCompName() + "Ctor" + javaCodeFileExtension);

    if (fsService.isFileExist(originalFilePath)) {
      return;
    }
    //此处判断用来兼容文件夹下已经有对应的生成文件情形
    if ((fsService.isFileExist(filePathExe)) && (!fsService.isFileExist(filePathCommon))) {
      return;
    }
    if (!fsService.isFileExist(filePathExe)) {
      iFsService.createFile(filePathExe, codeGen.generateExecute());
    }
    // be动作执行GenerateCommon(),commonDtm不执行
    if (codeGen.getIsCommonGenerate()) {
      iFsService.createFile(filePathExe, codeGen.generateCommon());
    }
  }

  /**
   * 兼容原来情况，修改引用的程序集
   *
   * @param filePath
   */
  private void JavaReplace(String filePath) {
    String oldValue = "import com.inspur.Gsp.bef.entity.changeset;";
    String newValue = "import inspur.Gsp.cef.entity.changeset;";
    String content = null;
    try {
      content = fsService.fileRead(filePath);
    } catch (IOException e) {
      throw new RuntimeException("文件读取失败！");
    }
    if (!content.contains(oldValue)) {
      return;
    }
    codeContent = content.replace(oldValue, newValue);
    fsService.fileUpdate(filePath, codeContent);
  }

  private void JavaGenerateVariableDtms(CommonDtmCollection dtms, String dirPath,
      ArrayList<String> actionList) {

    for (CommonDetermination dtm : dtms) {
      if (dtm.getIsRef() || !dtm.getIsGenerateComponent() || !actionList.contains(dtm.getCode())) {
        continue;
      }
      JavaGenerateVariableDtm(dtm, dirPath);
    }
  }

  private void JavaGenerateVariableDtm(CommonDetermination dtm, String dirPath) {
    JavaCommonDeterminationGenerator gen = new JavaCommonDeterminationGenerator(be, dtm,
        compAssemblyName, relativePath);
    JavaGenerateVariableDtmFile(gen, dirPath);
  }

  private void javaGenerateBeanConfigFile() {
    if (this.extendProperties.size() == 0) {
      return;
    }
    String filePath = CheckInfoUtil.getCombinePath(GetJavaCompModulePath(),
        be.getCode() + JavaCompCodeNames.CefConfigFileSuffix + javaCodeFileExtension);
    JavaBeanConfigGenerator beanConfigGen = new JavaBeanConfigGenerator(be, this.extendProperties,
        compAssemblyName);
    if (fsService.isFileExist(filePath)) {
      String content = null;
      try {
        content = fsService.fileRead(filePath);
      } catch (IOException e) {
        throw new RuntimeException("读取文件" + filePath + "失败");
      }
      fsService.fileUpdate(filePath, beanConfigGen.update(content), false);
    } else {
      iFsService.createFile(filePath, beanConfigGen.generate());
      addSpringFactoriesProperty();
    }
  }

  private void addSpringFactoriesProperty() {
    GenerateService service = SpringBeanUtils.getBean(GenerateService.class);
    MetadataProjectService servicePath = SpringBeanUtils.getBean(MetadataProjectService.class);
    String compModulePath = servicePath.getJavaCompProjectPath(relativePath);
    String beanConfigPath =
        JavaCodeGeneratorUtil.ConvertImportPackage(be.getComponentAssemblyName()) + "." + be
            .getCode() + JavaCompCodeNames.CefConfigFileSuffix;
    service.addProperties(compModulePath, JavaCompCodeNames.SPRING_FACTORIES_PROPERTYKEY,
        beanConfigPath);
  }
}
